#include "flash.h"
#include "ch32v30x_gpio.h"


#define  W25Q128_READ_DATA_CMD         (0x03)
#define  W25Q128_WRITE_ENABLE_CMD      (0x06)
#define  W25Q128_WRITE_DISENABLE_CMD   (0x04)
#define  W25Q128_WRITE_PAGE_CMD        (0x02)
#define  W25Q128_READ_STATUS_REG_1_CMD (0x05)
#define  W25Q128_SECTOR_ERASE_CMD      (0x20)

    /* *
     * PE0 -> CS    修改后  PE2 -> CS
     * PE1 -> CLK   修改后  PE3 -> CLK
     * PE2 -> MOSI  修改后  PE4 -> MOSI
     * PE3 -> MISO  修改后  PE5 -> MISO
     * 支持模式0和模式3
     * SPI模式区分 :
     *
     * 时钟极性(CPOL)和时钟相位(CPHA)区分
     * ‌CPOL=0, CPHA=0‌：这是SPI的第一个工作模式，其中串行时钟（SCK）的空闲状态为低电平，数据在SCK的上升沿被采样
     * CPOL=0, CPHA=1‌：在这种模式下，SCK的空闲状态仍为低电平，但数据在SCK的下降沿被采样
     * CPOL=1, CPHA=0‌：这种模式下，SCK的空闲状态变为高电平，数据在SCK的上升沿被采样
     * CPOL=1, CPHA=1‌：这是最后一种模式，其中SCK的空闲状态为高电平，数据在SCK的下降沿被采样
     * */

#define SPI_CS_HIGH (GPIO_SetBits(GPIOE, GPIO_Pin_2))
#define SPI_CS_LOW  (GPIO_ResetBits(GPIOE, GPIO_Pin_2))

#define SPI_CLK_HIGH (GPIO_SetBits(GPIOE, GPIO_Pin_3))
#define SPI_CLK_LOW  (GPIO_ResetBits(GPIOE, GPIO_Pin_3))

#define SPI_MOSI_HIGH (GPIO_SetBits(GPIOE, GPIO_Pin_4))
#define SPI_MOSI_LOW  (GPIO_ResetBits(GPIOE, GPIO_Pin_4))

#define SPI_MISO_VAL  (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_5))

static uint8_t flash_read_writeByte(uint8_t date);

void flash_hardware_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //打开GPIOE的时钟

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode  =GPIO_Mode_IPU;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    //user mode 1 CPOL=0, CPHA=0
    SPI_CS_HIGH;
    SPI_CLK_LOW;
    printf("falsh id = 0x%x\r\n",flash_read_w25q128_id());
}


static uint8_t flash_read_writeByte(uint8_t date)
{
    uint8_t len = 0;
    uint8_t readByte = 0;

    for (len = 0;len < 8;len++) {
        SPI_CLK_LOW;
        if (date & 0x80)
            SPI_MOSI_HIGH;
        else
            SPI_MOSI_LOW;
        date <<= 1;
        SPI_CLK_HIGH;
        readByte <<= 1;
        if (SPI_MISO_VAL) {
            readByte |= 0x01;
        }
    }
    SPI_CLK_LOW; //空闲时钟为低电平

    return readByte;
}


uint16_t flash_read_w25q128_id(void)
{
    uint16_t product_id = 0;
    SPI_CS_LOW; //拉低片选
    flash_read_writeByte(0x90); //发送0x90
    flash_read_writeByte(0x00);
    flash_read_writeByte(0x00);
    flash_read_writeByte(0x00);
    product_id |= flash_read_writeByte(0xFF) << 8 ;
    product_id |= flash_read_writeByte(0xFF);
    SPI_CS_HIGH; //释放片选信号
    return product_id;
}

static void flash_write_enable(void)
{
    SPI_CS_LOW; //拉低片选
    flash_read_writeByte(W25Q128_WRITE_ENABLE_CMD); //发送0x06
    SPI_CS_HIGH; //释放片选信号
}

//static void flash_write_disenable(void)
//{
//    SPI_CS_LOW; //拉低片选
//    flash_read_writeByte(W25Q128_WRITE_DISENABLE_CMD); //发送0x04
//    SPI_CS_HIGH; //释放片选信号
//}

/*
 * S7    S6 S5 S4 S3 S2 S1  S0
 * 保留                                                        擦除和写中位置1
 * */
uint8_t flash_read_status_registers(void)
{
    uint8_t status;
    SPI_CS_LOW; //拉低片选
    flash_read_writeByte(W25Q128_READ_STATUS_REG_1_CMD); //发送0x05读取包含S0位的寄存器值
    status = flash_read_writeByte(0xff);
    SPI_CS_HIGH; //释放片选信号
    return status;
}
static void flash_wait_action_done(void)
{
    while( (flash_read_status_registers() & 0x01) == 0x01);
}



/**
 * 对于W25Q128
 * 拥有256 个可擦除块
 * 4096个扇区
 *
 * 65536个页，每页大小为256个字节
 *
 * 一个块      = 16个扇区组成
 * 一个扇区 = 16个页组成
 * 一个页      = 256个字节组成
 *
 * 可编程的最小为1字节
 **/
void flash_read_bytes(uint8_t* buffer,uint32_t addr,uint16_t num)
{
    int m_loop;
    SPI_CS_LOW; //拉低片选
    flash_read_writeByte(W25Q128_READ_DATA_CMD); //发送读取数据的命令
    flash_read_writeByte( ( uint8_t ) (addr >> 16) );
    flash_read_writeByte( ( uint8_t ) (addr >> 8) );
    flash_read_writeByte( ( uint8_t )  addr );
    for (m_loop = 0;m_loop < num;m_loop++)
    {
        buffer[m_loop] = flash_read_writeByte(0xff);
    }
    SPI_CS_HIGH; //释放片选信号
}

int flash_read_littlefs(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, void *buffer, lfs_size_t size)
{
    if (block >= 4096) {
        return LFS_ERR_IO;
    }
    flash_read_bytes(buffer,block * 4096+ off,size);
    return LFS_ERR_OK;
}

/*
 * 写一页的数据
 * */
void flash_write_page(const uint8_t* buffer,uint32_t addr,uint16_t num)
{
    uint16_t m_loop;
    flash_write_enable();
    SPI_CS_LOW; //拉低片选
    flash_read_writeByte(W25Q128_WRITE_PAGE_CMD); //发送页写指令
    flash_read_writeByte( ( uint8_t ) (addr >> 16) );
    flash_read_writeByte( ( uint8_t ) (addr >> 8) );
    flash_read_writeByte( ( uint8_t )  addr );
    for(m_loop=0;m_loop<num;m_loop++)
        flash_read_writeByte(buffer[m_loop]);
    SPI_CS_HIGH; //释放片选信号
    flash_wait_action_done();//等待写入完成
}

void flash_write_bytes(const uint8_t* buffer,uint32_t addr,uint16_t num)
{
    uint16_t page_remain = 256 - addr % 256; //判断字节数是否能全部整页写
    if(num <= page_remain) page_remain = num; //不大于256
    while(1) {
        flash_write_page(buffer,addr,page_remain);
        if (num == page_remain) break;//所有需要写入的自己已经写完了
        else {
            buffer += page_remain; //要写入的数据偏移已经写入的数据
            addr += page_remain;   //写入的地址偏移已经写入的数据
            num -= page_remain;    //要写入的个数减少已经写入的个数
            if (num > 256) page_remain = 256;//一页只能写256个数据
            else page_remain = num; //未超过一页的大小则直接写入
        }

    }
}
int flash_write_littlefs(const struct lfs_config *c, lfs_block_t block,
           lfs_off_t off, const void *buffer, lfs_size_t size)
{
    if (block >= 4096) {
        return LFS_ERR_IO;
    }
    flash_write_bytes(buffer,block * 4096+ off,size);
    return LFS_ERR_OK;
}

void flash_erase_sector(uint32_t addr)
{
    uint32_t actu_addr = addr * 4096; //按照4K扇区擦除
//    printf("flash_erase_sector actu_addr = %x\r\n",addr);
    flash_write_enable();
    flash_wait_action_done();
    SPI_CS_LOW; //拉低片选
    flash_read_writeByte(W25Q128_SECTOR_ERASE_CMD); //发送擦除页指令
    flash_read_writeByte( ( uint8_t ) (actu_addr >> 16) );
    flash_read_writeByte( ( uint8_t ) (actu_addr >> 8) );
    flash_read_writeByte( ( uint8_t )  actu_addr );
    SPI_CS_HIGH; //释放片选信号
    flash_wait_action_done();
}
int flash_erase_littlefs(const struct lfs_config *c, lfs_block_t block)
{
//    printf("flash_erase_littlefs block = %d\r\n",block);
    if (block >= 4096) {
        return LFS_ERR_IO;
    }
    flash_erase_sector(block); //擦除一个扇区
    return LFS_ERR_OK;
}

int flash_sync_littlefs(const struct lfs_config *c)
{
    return LFS_ERR_OK;
}
